//
//  network_utils.cpp
//  mac_ping_network
//
//  Created by Constantine Mars on 5/14/13.
//  Copyright (c) 2013 Constantine Mars. All rights reserved.
//


#include <string>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <netdb.h>
#include "network_utils.h"
#include "ping_observer_impl.h"
#include "common/ScopedAction.h"

namespace io = boost::iostreams;
const int g_LDAPPort = 389;

class not_implemented: std::exception
{
public:
    const char* what() { return "not implemented"; }
};

std::string popen2string(const char* pipe)
{
    FILE* file = popen(pipe, "r");
    if (!file)
        throw std::runtime_error("failed to popen.");
    boost::shared_ptr<FILE> pipe_guard(file, pclose);
    
    io::stream<io::file_descriptor_source> in(io::file_descriptor_source(fileno(file), io::never_close_handle));
    
    std::string line;
    std::getline(in, line);
    
    return line;
}

long long getPingTimeout(const char* server_addr, unsigned int *expires_at)
{
    long long ping_timeout = 0;
    const unsigned int attempts = 5;
    
    PingObserverImpl observer(attempts, &ping_timeout);
    *expires_at = Ping(server_addr, attempts, &observer);
    
    return ping_timeout;
}

bool isDomainControllerReachable()
{
    std::string domain_name = popen2string("dsconfigad -show | grep \"Active Directory Domain\" | awk \'{print $5}\'"); // domain name
    bool reachable = false;
    unsigned int expires_at = 0;
    long long timeout = 0;
    
    try
    {
        timeout = getPingTimeout(domain_name.c_str(),
                                 //"dev.local",
                                 //"dtt.local", // One of these domains is reachable another - not
                                 &expires_at); 
        reachable = true;
        std::clog << "timeout=" << timeout << "; when expires_at=" << expires_at << std::endl;
    }
    catch (const std::exception& e)
    {
//        std::string what = e.what();
//        if (what.find("resolve: ") == 0)
//        {
//            std::clog << "resolve error" << std::endl;
//        }
        
        throw;
    }
    
    return (reachable && timeout >= 0 && timeout < expires_at);
}

//namespace
//{
//    void lclose(int *s)
//    {
//        std::clog << ">> close" << std::endl;
//        close(*s);
//    }
//}

bool isDomainControllerReachable2(const char* domain)
{
    // This variant is more efficient because of speed
    
    using namespace std;
    bool is_open = false;    
    string domain_name = ( domain ? domain : popen2string("dsconfigad -show | grep \"Active Directory Domain\" | awk \'{print $5}\'") ); // domain name

    struct hostent *hp = gethostbyname(domain_name.c_str());
    
    if (hp != NULL)
    {
        printf("forward resolved: %s = ", hp->h_name);
        
        for (unsigned int i = 0; hp -> h_addr_list[i] != NULL; ++i)
        {
            
            const char* addr = inet_ntoa( *( struct in_addr*)( hp -> h_addr_list[i]));
            printf( "%s", addr);
            
            {
                sockaddr_in myaddr;
                myaddr.sin_family = AF_INET;
                myaddr.sin_port = htons(g_LDAPPort);
                inet_aton(addr, &myaddr.sin_addr);
                
                int s = socket(PF_INET, SOCK_STREAM, 0);
                if(s == -1)
                {
                    printf(" (failed to create socket)\n");
                    continue;
//                    return false;
                }
                
//                boost::shared_ptr<int> socket_guard(&s, lclose);
                ScopedAction socket_guard(boost::bind(close, s));
                
                int st = connect(s, reinterpret_cast<sockaddr*>(&myaddr), sizeof(myaddr));
                
                is_open = (st == 0);
            printf("(%s) ", (is_open ? "ok" : "failed") );
            }
        }
        printf("\n");
    }

    return is_open;
}